package com.heima.wemedia.service.impl;

import com.alibaba.fastjson.JSONArray;
import com.aliyuncs.utils.StringUtils;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.heima.common.constants.message.WmNewsUpDownConstants;
import com.heima.common.dtos.PageResult;
import com.heima.common.dtos.ResponseResult;
import com.heima.common.enums.AppHttpCodeEnum;
import com.heima.common.exception.LeadException;
import com.heima.common.threadlocal.UserThreadLocalUtils;
import com.heima.common.util.JsonUtils;
import com.heima.model.admin.dtos.NewsAuthRequestDTO;
import com.heima.model.admin.dtos.NewsAuthResponseDTO;
import com.heima.model.media.dtos.WmNewsContentItem;
import com.heima.model.media.dtos.WmNewsDownOrUpDTO;
import com.heima.model.media.dtos.WmNewsDto;
import com.heima.model.media.dtos.WmNewsPageReqDto;
import com.heima.model.media.enums.WmNewsMaterialTypeEnum;
import com.heima.model.media.enums.WmNewsStatusEnum;
import com.heima.model.user.dtos.AuthDTO;
import com.heima.wemedia.mapper.WmNewsMapper;
import com.heima.wemedia.pojo.WmMaterial;
import com.heima.wemedia.pojo.WmNews;
import com.heima.wemedia.pojo.WmNewsMaterial;
import com.heima.wemedia.service.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.ObjectUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

/**
 * <p>
 * 自媒体图文内容信息表 服务实现类
 * </p>
 *
 * @author HM
 * @since 2023-03-22
 */
@Slf4j
@Service
public class WmNewsServiceImpl extends ServiceImpl<WmNewsMapper, WmNews> implements WmNewsService {

    @Autowired
    WmNewsMaterialService wmNewsMaterialService;

    @Autowired
    WmUserService userService;

    @Autowired
    WmMaterialService wmMaterialService;

    @Autowired
    AuditService auditService;

    @Autowired
    KafkaTemplate<String, String> kafkaTemplate;


    /**
     * 根据提供的条件分页查询当前自媒体用户的自媒体文章列表
     *
     * @param dto
     * @return
     */
    @Override
    public PageResult<WmNews> findByPage(WmNewsPageReqDto dto) {
        Integer userId = UserThreadLocalUtils.getUserId();
        if (userId == null || userId <= 0) {
            // 如果ThreadLocal中查询不到UserId, 认为是没有登录,同时抛出异常
            log.error("UserThreadLocalUtils 获取UserId出现异常! userId为: {}", userId);
            throw new LeadException(AppHttpCodeEnum.NEED_LOGIN);
        }
        // 构建分页查询的查询条件
        LambdaQueryWrapper<WmNews> queryWrapper = buildPageQueryWrapper(dto, userId);
        Page<WmNews> ipage = new Page<>(dto.getPage(), dto.getSize());
        this.page(ipage, queryWrapper);


        return new PageResult<>(dto, ipage.getTotal(), ipage.getRecords());
    }


    /**
     * 删除自媒体文章
     * @param id
     */
    @Override
    public void deleteById(Integer id) {
        //获取当前用户id
        Integer userId = UserThreadLocalUtils.getUserIdAndCheckLogin();

        //获取自媒体信息,
        WmNews wmNewsDb = this.getById(id);
        if (wmNewsDb == null) {
            //为空
            throw new LeadException(AppHttpCodeEnum.DATA_NOT_EXIST);
        }
        // 如果数据库中的userId和当前登录的用户id不同, 则抛出异常
        if (!wmNewsDb.getWmUserId().equals(userId)) {
            throw new LeadException(AppHttpCodeEnum.NO_OPERATOR_AUTH);
        }
        //判断文章是否发布
        if (ObjectUtils.equals(wmNewsDb.getStatus(), WmNewsStatusEnum.PUBLISHED)) {   //9 已发布
            //发布
            //判断是否下架
            if (!wmNewsDb.getEnable()) {     // 0 是false  下架  1 是true 上架
                log.info("文章已下架");
                this.doRemove(id);
            } else {
                log.info("文章未下架");
                throw new LeadException(AppHttpCodeEnum.DATA_CAN_NOT_DEL_ENABLE);
            }

        } else {
            //未发布
            this.doRemove(id);
        }

    }

    /**
     * 文章删除
     */
    private void doRemove(Integer id) {
        //删除
        LambdaQueryWrapper<WmNewsMaterial> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(WmNewsMaterial::getNewsId, id);
        List<WmNewsMaterial> newsMaterials = wmNewsMaterialService.list(queryWrapper);
        if (!CollectionUtils.isEmpty(newsMaterials)) {
            removeById(id);
        } else {
            throw new LeadException(AppHttpCodeEnum.NO_OPERATOR_AUTH);
        }
    }

    /**
     * 自媒体用户登陆后, 新增, 修改, 发布自媒体文章
     *
     * @param wmNewsDto 前端DTO对象
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void submit(WmNewsDto wmNewsDto) {
        // 保存文章信息
        // 保存文章和素材关系

        // 健壮性校验
        if (wmNewsDto == null) {
            throw new LeadException(AppHttpCodeEnum.PARAM_REQUIRE);
        }

        String images = this.imagesToString(wmNewsDto.getImages());
        // 如果 newsId == null, 则视为新增, 否则为修改
        if (wmNewsDto.getId() == null) {
            WmNews wmNews = this.buildWmNews(wmNewsDto, images);
            this.save(wmNews);
            // 执行save方法之后存在id了
            wmNewsDto.setId(wmNews.getId());
            List<WmNewsMaterial> wmNewsMaterialList = this.buildNewsMaterials(wmNewsDto);

            wmNewsMaterialService.saveBatch(wmNewsMaterialList);
        } else {
            // 如果id不为空, 则执行修改
            // 删除所有素材关联关系
            wmNewsMaterialService.deleteByWmNewsId(wmNewsDto.getId());
            // 执行修改操作
            this.updateById(this.buildWmNews(wmNewsDto, images));
            // 执行保存操作
            wmNewsMaterialService.saveBatch(this.buildNewsMaterials(wmNewsDto));
        }
        if (wmNewsDto.getDraft()) {
            // 如果提交为草稿, 则直接保存return即可
            return;
        }

        log.info("文章保存完成, 开始进行审核");
        // 异步审核
        auditService.auditWmNewsAsync(UserThreadLocalUtils.getUserId(), images, wmNewsDto,WmNewsStatusEnum.SUBMIT);
    }

    /**
     * 根据自媒体文章id查询自媒体文章人工审核详情
     *
     * @param id
     * @return
     */
    @Override
    public NewsAuthResponseDTO findNewsAuthResponseDTOByWmNewsId(Integer id) {
        LambdaQueryWrapper<WmNews> queryWrapper = new LambdaQueryWrapper<>();
        // 必须是人工审核的自媒体文章
        queryWrapper.eq(WmNews::getStatus, WmNewsStatusEnum.ADMIN_AUTH.getCode());
        queryWrapper.eq(WmNews::getId, id);
        return buildNewsAuthResponseDtoByWmNews(this.getOne(queryWrapper));
    }

    /**
     * 根据文章的审核结果更新文章信息
     *
     * @param wmNewsId
     * @param reason     审核信息
     * @param statusEnum 审核状态
     * @param articleId
     */
    @Override
    public void updateWmNewsWithAuthResult(Integer wmNewsId, String reason, WmNewsStatusEnum statusEnum, Long articleId) {
        WmNews updateWmNews = new WmNews();
        updateWmNews.setId(wmNewsId);
        updateWmNews.setStatus(statusEnum.getCode());
        updateWmNews.setReason(reason);
        updateWmNews.setArticleId(articleId);
        this.updateById(updateWmNews);
    }

    /**
     * 前台根据文章id查询自媒体文章详情
     *
     * @param id
     * @return
     */
    @Override
    public ResponseResult<WmNews> getByWmNewsId(String id) {
        // 校验用户是否登录
        Integer userId = UserThreadLocalUtils.getUserIdAndCheckLogin();
        LambdaQueryWrapper<WmNews> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(WmNews::getWmUserId, userId);
        queryWrapper.eq(WmNews::getId, id);
        WmNews wmNewsDB = this.getOne(queryWrapper);

        if (wmNewsDB == null) {
            log.error("查询单个文章信息时没有查询到 可能存在用户越权访问行为,wmUserId = {},wmNewsId = {}", userId, id);
            throw new LeadException(AppHttpCodeEnum.NO_OPERATOR_AUTH);
        }
        return ResponseResult.ok(wmNewsDB);
    }

    /**
     * 自媒体文章审核失败(人工审核)
     *
     * @param dto
     */
    @Override
    public void authFail(AuthDTO dto) {
        if (dto == null){
            log.error("AuthDTO对象为空");
            throw new LeadException(AppHttpCodeEnum.PARAM_INVALID);
        }
        // 修改审核状态
        this.updateWmNewsWithAuthResult(dto.getId(), dto.getMsg(), WmNewsStatusEnum.FAIL, null);
    }

    /**
     * 人工审核成功 feign调用
     * 修改wmNews状态, 异步生成自媒体文章
     *
     * @param dto
     */
    @Override
    public void authPass(AuthDTO dto) {
        if (dto == null || dto.getId() == null) {
            log.error("AuthDTO 数据为空 或AuthDTO.id 为空");
            throw new LeadException(AppHttpCodeEnum.PARAM_INVALID);
        }

        WmNews wmNewsDb = this.getById(dto.getId());
        if (wmNewsDb == null) {
            log.error("无法根据WmNewsId查询到WmNews信息");
            throw new LeadException(AppHttpCodeEnum.DATA_NOT_EXIST);
        }

        // 异步创建Article文章
        auditService.auditWmNewsAsync(wmNewsDb.getWmUserId(), "", this.buildWmNewsDto(wmNewsDb), WmNewsStatusEnum.ADMIN_SUCCESS);
    }

    /**
     * 根据标题分页查询自媒体文章, 仅用于feign调用
     *
     * @param dto
     * @return
     */
    @Override
    public Page<NewsAuthResponseDTO> findByPageWithFeign(NewsAuthRequestDTO dto) {
        log.info("接收到dto参数为: {}", dto);
        if (dto == null) {
            return null;
        }
        // 构建查询条件并进行分页查询
        Page<WmNews> ipage = this.getWmNewsPage(dto);
        Page<NewsAuthResponseDTO> wmNewsDtoPage = new Page<>();
        wmNewsDtoPage.setTotal(ipage.getTotal());

        // 将WmNewsList转为DTO对象集合,并为作者名称赋值
        List<NewsAuthResponseDTO> newsAuthResponseDTOList = ipage.getRecords()
                .stream()
                .map(this::buildNewsAuthResponseDtoByWmNews)
                .collect(Collectors.toList());

        wmNewsDtoPage.setRecords(newsAuthResponseDTOList);
        return wmNewsDtoPage;
    }






    /**
     * 自媒体文章上下架,发送kafka消息通知其他微服务
     *
     * @param dto
     * @return
     */
    @Override
    public void downOrUp(WmNewsDto dto) {
        Integer wmUserId = UserThreadLocalUtils.getUserIdAndCheckLogin();
        // 从数据库中查询wmNews信息
        LambdaQueryWrapper<WmNews> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(WmNews::getId, dto.getId());
        queryWrapper.eq(WmNews::getWmUserId, wmUserId);
        WmNews wmNewsDb = this.getOne(queryWrapper);
        // 状态校验
        if (wmNewsDb == null) {
            throw new LeadException(AppHttpCodeEnum.DATA_NOT_EXIST);
        }
        // 只有已发布的才允许做上下架
        if (wmNewsDb.getStatus() != 9) {
            throw new LeadException(AppHttpCodeEnum.PARAM_INVALID);
        }
        if (wmNewsDb.getEnable().equals(dto.getEnable())) {
            // 如果前端传递的上下架状态, 和数据库中相同, 则不需要进行操作.
            return;
        }
        // 更新WmNews状态
        LambdaUpdateWrapper<WmNews> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.eq(WmNews::getId, dto.getId());
        updateWrapper.set(WmNews::getEnable, dto.getEnable());
        this.update(updateWrapper);

        WmNewsDownOrUpDTO newsDownOrUpDTO = new WmNewsDownOrUpDTO();
        newsDownOrUpDTO.setWmNewsId(wmNewsDb.getId());
        newsDownOrUpDTO.setArticleId(wmNewsDb.getArticleId());
        newsDownOrUpDTO.setIsDown(!dto.getEnable());
        // 发送kafka消息
        kafkaTemplate.send(WmNewsUpDownConstants.WM_NEWS_UP_OR_DOWN_TOPIC, JsonUtils.toString(newsDownOrUpDTO));
    }

    /**
     * 根据请求的内容正文来创建  List<WmNewsMaterial>
     *
     * @param images 标题内容
     * @return List<WmNewsMaterial>
     */
    private List<WmNewsMaterial> buildNewsMaterialsByImages(List<String> images, Integer wmNewsId) {
        if (CollectionUtils.isEmpty(images)) {
            return null;
        }

        // 根据图片的url查询素材表中的所有数据
        List<WmMaterial> wmMaterialList = wmMaterialService.findByImages(images);


        // 使用Stream 流 来 将  List<WmMaterial> 转换为 List<WmNewsMaterial>
        return wmMaterialList.stream().map(item -> {
            WmNewsMaterial wmNewsMaterial = new WmNewsMaterial();
            wmNewsMaterial.setMaterialId(item.getId());
            wmNewsMaterial.setNewsId(wmNewsId);
            wmNewsMaterial.setType(WmNewsMaterialTypeEnum.IMAGE.getType());
            wmNewsMaterial.setOrd(1);
            return wmNewsMaterial;
        }).collect(Collectors.toList());

    }

    /**
     * 构建WmNewsMaterial对象
     *
     * @param wmNewsDto wmNewsDTO对象
     * @return
     */
    private List<WmNewsMaterial> buildNewsMaterials(WmNewsDto wmNewsDto) {

        // 声明一个 WmNewsMaterial 集合
        List<WmNewsMaterial> wmNewsMaterialList = new ArrayList<>();

        Integer newsId = wmNewsDto.getId();

        // 解析正文并添加到集合中
        List<WmNewsMaterial> listByContent = this.buildNewsMaterialsByContent(wmNewsDto.getContent(), newsId);
        if (!CollectionUtils.isEmpty(listByContent)) {
            wmNewsMaterialList.addAll(listByContent);
        }

        // 解析标题图片并添加到集合中
        List<WmNewsMaterial> listByImg = this.buildNewsMaterialsByImages(wmNewsDto.getImages(), newsId);
        if (!CollectionUtils.isEmpty(listByImg)) {
            wmNewsMaterialList.addAll(listByImg);
        }

        return wmNewsMaterialList;
    }

    /**
     * 构建查询条件,并进行分页查询
     *
     * @param dto
     * @return
     */
    private Page<WmNews> getWmNewsPage(NewsAuthRequestDTO dto) {
        LambdaQueryWrapper<WmNews> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.like(StringUtils.isNotEmpty(dto.getTitle()), WmNews::getTitle, dto.getTitle());
        // 只查询人工审核的部分
        queryWrapper.eq(WmNews::getStatus, WmNewsStatusEnum.ADMIN_AUTH.getCode());
        // 按照创建时间倒序
        queryWrapper.orderByDesc(WmNews::getPublishTime);

        Page<WmNews> ipage = new Page<>(dto.getPage(), dto.getSize());
        this.page(ipage, queryWrapper);
        return ipage;
    }


    /**
     * 根据WmNews构建NewsAuthResponseDTO对象
     *
     * @param wmNews
     * @return
     */
    private NewsAuthResponseDTO buildNewsAuthResponseDtoByWmNews(WmNews wmNews) {
        NewsAuthResponseDTO dto = new NewsAuthResponseDTO();
        dto.setId(wmNews.getId());
        dto.setUserId(wmNews.getWmUserId());
        dto.setTitle(wmNews.getTitle());
        dto.setContent(wmNews.getContent());
        dto.setType(wmNews.getType());
        dto.setChannelId(wmNews.getChannelId());
        dto.setLabels(wmNews.getLabels());
        dto.setCreatedTime(wmNews.getCreatedTime());
        dto.setSubmitedTime(wmNews.getSubmitedTime());
        dto.setStatus(wmNews.getStatus());
        dto.setPublishTime(wmNews.getPublishTime());
        dto.setReason(wmNews.getReason());
        dto.setArticleId(wmNews.getArticleId());
        dto.setImages(wmNews.getImages());
        dto.setEnable(wmNews.getEnable() ? 1 : 0);
        // 查询WmUser信息并赋值
        dto.setAuthorName(userService.getById(wmNews.getWmUserId()).getName());

        return dto;
    }


    /**
     * 根据请求的内容正文来创建  List<WmNewsMaterial>
     *
     * @param content 文章内容
     * @return List<WmNewsMaterial>
     */
    private List<WmNewsMaterial> buildNewsMaterialsByContent(String content, Integer wmNewsId) {
        if (StringUtils.isEmpty(content)) {
            return null;
        }
        // 解析JSON,获取集合, 并获取只包含 type 为image的 item
        List<WmNewsContentItem> wmNewsContentItemsByImages =
                WmNewsContentItem.getImageItems(JSONArray.parseArray(content, WmNewsContentItem.class));

        // 将 WmNewsContentItem集合转为 WmNewsMaterial集合并返回
        return wmNewsContentItemsByImages.stream().map(item -> {
            WmNewsMaterial wmNewsMaterial = new WmNewsMaterial();
            wmNewsMaterial.setNewsId(wmNewsId);
            wmNewsMaterial.setMaterialId(item.getId());
            wmNewsMaterial.setType(WmNewsMaterialTypeEnum.CONTENT.getType());
            wmNewsMaterial.setOrd(1);

            return wmNewsMaterial;
        }).collect(Collectors.toList());

    }
    /**
     * 将图片url集合转为字符串
     *
     * @param images image url的集合
     * @return 转换后的字符串
     */
    private String imagesToString(List<String> images) {
        if (CollectionUtils.isEmpty(images)) {
            return null;
        }
        // 将集合转为string类型
        return String.join(",", images);
    }


    /**
     * 构建用于保存文章表的文章对象
     *
     * @param wmNewsDto wmNewsDto
     * @return
     */
    private WmNews buildWmNews(WmNewsDto wmNewsDto, String images) {
        WmNews wmNews = new WmNews();
        // id
        wmNews.setId(wmNewsDto.getId());
        // WmUserId
        wmNews.setWmUserId(UserThreadLocalUtils.getUserIdAndCheckLogin());
        //文章布局
        wmNews.setType(wmNewsDto.getType());
        // 文章标签
        wmNews.setLabels(wmNewsDto.getLabels());
        // 文章当前状态
        wmNews.setStatus(wmNewsDto.getStatus());
        // 文章定时发布时间
        wmNews.setPublishTime(wmNewsDto.getPublishTime());
        // 频道id
        wmNews.setChannelId(wmNewsDto.getChannelId());
        // 文章标题
        wmNews.setTitle(wmNewsDto.getTitle());
        // 封面图片信息
        wmNews.setImages(images);

        // 文章图文内容
        wmNews.setContent(wmNewsDto.getContent());
        if (!wmNewsDto.getDraft()) {
            wmNews.setSubmitedTime(new Date());
        }
        return wmNews;
    }

    /**
     * 构建分页查询的查询条件的方法
     *
     * @param dto
     * @param userId
     * @return
     */
    private LambdaQueryWrapper<WmNews> buildPageQueryWrapper(WmNewsPageReqDto dto, Integer userId) {
        LambdaQueryWrapper<WmNews> queryWrapper = new LambdaQueryWrapper<>();
        // 数据库中的status和前端传入的status参数不能一一对应 通过getDbStatusList 进行转换.
        if (dto.getStatus() != null && dto.getStatus() != -1) {
            queryWrapper.in(WmNews::getStatus, getDbStatusList(dto.getStatus()));
        }
        queryWrapper.eq(dto.getChannelId() != null, WmNews::getChannelId, dto.getChannelId());
        queryWrapper.eq(WmNews::getWmUserId, userId);
        // 文章标题关键字查询
        queryWrapper.like(dto.getKeyword() != null, WmNews::getTitle, dto.getKeyword());
        queryWrapper.ge(dto.getBeginPubDate() != null, WmNews::getPublishTime, dto.getBeginPubDate());
        queryWrapper.le(dto.getEndPubDate() != null, WmNews::getPublishTime, dto.getEndPubDate());
        return queryWrapper;
    }

    /**
     * 将前端传入的status转为数据库的status集合
     *
     * @param status
     * @return
     */
    private List<Integer> getDbStatusList(Integer status) {
        List<Integer> statusList = new ArrayList<>();
        switch (status) {
            case 0:
                statusList.add(0);
                break;
            case 1:
                statusList.add(1);
                statusList.add(3);
                break;
            case 2:
                statusList.add(2);
                break;
            case 4:
                statusList.add(4);
                statusList.add(8);
                statusList.add(9);
                break;
            default:
                log.error("status参数非法! status参数为: {}", status);
                throw new LeadException(AppHttpCodeEnum.PARAM_INVALID);
        }
        return statusList;
    }


    /**
     * 根据wmNews对象构建WmNewsDto
     *
     * @param wmNewsDb
     * @return
     */
    private WmNewsDto buildWmNewsDto(WmNews wmNewsDb) {
        WmNewsDto dto = new WmNewsDto();
        dto.setId(wmNewsDb.getId());
        dto.setTitle(wmNewsDb.getTitle());
        dto.setChannelId(wmNewsDb.getChannelId());
        dto.setLabels(wmNewsDb.getLabels());
        dto.setPublishTime(wmNewsDb.getPublishTime());
        dto.setContent(dto.getContent());
        dto.setType(wmNewsDb.getType());
        dto.setDraft(false);
        dto.setStatus(wmNewsDb.getStatus());
        dto.setImages(this.getImagesByImageString(wmNewsDb.getImages()));
        dto.setReason(wmNewsDb.getReason());
        dto.setEnable(wmNewsDb.getEnable());
        dto.setContent(wmNewsDb.getContent());
        return dto;
    }

    /**
     * 根据标题图片的url字符串获取标题图片url集合
     * @param images
     * @return
     */
    private List<String> getImagesByImageString(String images) {
        if (StringUtils.isEmpty(images) || !images.contains(",")){
            return null;
        }

        return Arrays.asList(images.split(","));

    }
}
